#include <vector>
#include <cmath>
#include <math.h>
#include <complex>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "expr.hpp"
#include "catch.hpp"

std::ostream & operator<<(std::ostream& out, const expr& e) {
    out << "a";
    return out;
}
class expr2: public expr{
    double result=0;
    std::string expresion="";
private:
     void virtual write(std::ostream& out)const{
         
    }
public:
    expr2(){
        result=0;
        
    }
    expr2(double a){
        result=a;
    }
    void assignResult(double res){
        result=res;
    }
   
    double virtual evaluate()const{
        return result;
    
    };

};
std::unique_ptr<expr> create_expression_tree(const std::string& expression) {
    std::vector<std::string> vectorOfelements;
    std::vector<char> vectorOfoperands;
    vectorOfoperands.reserve(expression.length());
    vectorOfelements.reserve(expression.length());
    std::string bufferstring="";
   
    
    for(int i=0;i<expression.length();i++){
       
        if((expression.at(i)>=48&&expression.at(i)<=57)||expression.at(i)==46){
            bufferstring+=expression.at(i);
        }else{
            vectorOfelements.push_back(bufferstring);
            bufferstring="";
            
            if(expression.at(i)=='+'){ 
                while(true){
                    if(vectorOfoperands.empty()){break;}
                    if(vectorOfoperands.back()=='/'|| vectorOfoperands.back()=='^'||vectorOfoperands.back()=='-'||vectorOfoperands.back()=='+' || vectorOfoperands.back()=='*'){
                        bufferstring="";
                        bufferstring+=vectorOfoperands.back();
                        vectorOfelements.push_back(bufferstring);
                        vectorOfoperands.pop_back();
                        bufferstring="";
                    }else{break;}
                    }
                vectorOfoperands.push_back(expression.at(i));
            }
            if(expression.at(i)=='*'){
                while(true){
                     if(vectorOfoperands.empty()){break;}
                    if(vectorOfoperands.back()=='/'|| vectorOfoperands.back()=='^'||vectorOfoperands.back()=='*'){
                        bufferstring="";
                        bufferstring+=vectorOfoperands.back();
                        vectorOfelements.push_back(bufferstring);
                        vectorOfoperands.pop_back();
                        bufferstring="";
                    }else{break;}
                    }
                vectorOfoperands.push_back(expression.at(i));
            }
            if(expression.at(i)=='-'){
                while(true){
                        if(vectorOfoperands.empty()){break;}
                        if(vectorOfoperands.back()=='/'|| vectorOfoperands.back()=='^'||vectorOfoperands.back()=='-'||vectorOfoperands.back()=='+' || vectorOfoperands.back()=='*'){
                            bufferstring="";
                            bufferstring+=vectorOfoperands.back();
                            vectorOfelements.push_back(bufferstring);
                            vectorOfoperands.pop_back();
                            bufferstring="";
                        }else{break;}
                        }
                 vectorOfoperands.push_back(expression.at(i));
            }
            if(expression.at(i)=='/'){ 
                while(true){
                     if(vectorOfoperands.empty()){break;}
                    if(vectorOfoperands.back()=='/'|| vectorOfoperands.back()=='^'||vectorOfoperands.back()=='*'){
                        bufferstring="";
                        bufferstring+=vectorOfoperands.back();
                        vectorOfelements.push_back(bufferstring);
                        vectorOfoperands.pop_back();
                        bufferstring="";
                    }else{break;}
                    }
                vectorOfoperands.push_back(expression.at(i));
            }
            if(expression.at(i)=='^'){
                 vectorOfoperands.push_back(expression.at(i));
            }
            if(expression.at(i)=='('){vectorOfoperands.push_back('(');}
            if(expression.at(i)==')'){
                while(vectorOfoperands.back()!='('){
                        bufferstring="";
                        bufferstring+=vectorOfoperands.back();
                        vectorOfelements.push_back(bufferstring);
                        vectorOfoperands.pop_back();
                        bufferstring="";
                }
                vectorOfoperands.pop_back();
            
            }
        
        
        }
    
    
    }
    vectorOfelements.push_back(bufferstring);
    bufferstring="";
    
    
    while(vectorOfoperands.size()>0){
                        bufferstring="";
                        bufferstring+=vectorOfoperands.back();
                        vectorOfelements.push_back(bufferstring);
                        vectorOfoperands.pop_back();
                        bufferstring="";
    
    }
    double a,b,c;
    int counter=0,x=0,z=0,maxindex;
    for(int y=0;y<vectorOfelements.size();y++){
        if(vectorOfelements.at(y)!="")
        {counter++;}
    }
    
    maxindex=counter-1;
    std::string elementsArray[100];
    std::vector<std::string> mno;
    
    while(x<counter){
         if(vectorOfelements.at(z)!="")
        {
             elementsArray[x]=vectorOfelements.at(z);
             x++;
         }
         z++;
    }
    
    while(maxindex>0){
      
        for(int i=0;i<=maxindex;i++){
        
            
            if(elementsArray[i]=="+"){
                a=atof(elementsArray[i-2].c_str());
                b=atof(elementsArray[i-1].c_str());
                c=a+b;
                std::ostringstream crt;
                crt<< std::fixed << std::setprecision(100)<<c;
                
                elementsArray[i-2]=crt.str();
                maxindex-=2;
                for(int x=i-1;x<=maxindex;x++){
                    elementsArray[x]=elementsArray[x+2];
                
                }
                break;
               
            }
            if(elementsArray[i]=="-"){
                a=atof(elementsArray[i-2].c_str());
                b=atof(elementsArray[i-1].c_str());
                c=a-b;
                std::ostringstream crt;
                crt<< std::fixed << std::setprecision(100)<<c;
                
                elementsArray[i-2]=crt.str();
                maxindex-=2;
                for(int x=i-1;x<=maxindex;x++){
                    elementsArray[x]=elementsArray[x+2];
                
                }
                break;
            
            }
            if(elementsArray[i]=="*"){
                a=atof(elementsArray[i-2].c_str());
                b=atof(elementsArray[i-1].c_str());
                c=a*b;
                std::ostringstream crt;
                crt<< std::fixed << std::setprecision(100)<<c;
                
                elementsArray[i-2]=crt.str();
                maxindex-=2;
                for(int x=i-1;x<=maxindex;x++){
                    elementsArray[x]=elementsArray[x+2];
                
                }
                break;
            
            }
            if(elementsArray[i]=="/"){
             a=atof(elementsArray[i-2].c_str());
                b=atof(elementsArray[i-1].c_str());
                c=a/b;
                std::ostringstream crt;
                crt<< std::fixed << std::setprecision(100)<<c;
                
                elementsArray[i-2]=crt.str();
                maxindex-=2;
                for(int x=i-1;x<=maxindex;x++){
                    elementsArray[x]=elementsArray[x+2];
                
                }
                break;
            }
            if(elementsArray[i]=="^"){
                a=atof(elementsArray[i-2].c_str());
                b=atof(elementsArray[i-1].c_str());
                c=pow(a,b);
                std::ostringstream crt;
                crt<< std::fixed << std::setprecision(100)<<c;
                
                elementsArray[i-2]=crt.str();
                maxindex-=2;
                for(int x=i-1;x<=maxindex;x++){
                    elementsArray[x]=elementsArray[x+2];
                
                }
                break;
            
            }
            
            
        
        
        }
    
    
    
    
    }
    
    std::unique_ptr<expr> lslsl(new expr2((atof(elementsArray[0].c_str()))));
    return lslsl;
    
    
    
    
}